package boot

import (
	"context"
	"github.com/wendy512/iec104/pkg/logger"
	"os"
	"os/signal"
	"sort"
	"sync"
	"syscall"
)

// exceptionContext 系统异常通知Context
var exceptionContext context.Context

// exceptionCancel 系统异常通知Func
var exceptionCancel context.CancelFunc

// globalWaitGroup 全局Goroutine计数器
var globalWaitGroup sync.WaitGroup

// mutex 并发安全锁
var mutex sync.Mutex

// 系统异常处理函数列表，用于处理系统异常后的资源释放
type exceptionFunc struct {
	// 异常处理函数
	fc func()
	// 系统描述
	desc string
	// 系统等级
	level int
}

// exceptionFuncs 异常处理函数列表
var exceptionFuncs []exceptionFunc

// 初始化系统异常处理机制（接收到操作系统信号，如Kill）
func init() {
	exceptionFuncs = make([]exceptionFunc, 0)
	globalWaitGroup = sync.WaitGroup{}
	exceptionContext, exceptionCancel = context.WithCancel(context.Background())
	go handleException()
}

// handleOsSignal 处理操作系统信号
func handleOsSignal() {
	signCh := make(chan os.Signal, 1)
	// 注册需要监听的信号
	signal.Notify(signCh, syscall.SIGINT, syscall.SIGTERM)
	sign := <-signCh
	logger.Infof("iec104", "received os signal '%v', system is about to exit", sign)
	// 调用系统异常通知函数，所有调用ContextDone()的地方都将收到通知
	// 收到通知后进行服务停止，资源释放，退出业务处理函数
	exceptionCancel()
	// 等待各个处理流程退出
	globalWaitGroup.Wait()
	logger.Infof("iec104", "system exit success")
}

// handleException 系统异常平滑退出函数
func handleException() {
	<-exceptionContext.Done()
	mutex.Lock()
	defer mutex.Unlock()
	// 根据level进行调用排序递增排序
	sort.Slice(exceptionFuncs, func(i, j int) bool {
		return exceptionFuncs[i].level < exceptionFuncs[j].level
	})

	for _, f := range exceptionFuncs {
		logger.Infof("iec104", "start call '%v' exit function", f.desc)
		f.fc()
		logger.Infof("iec104", "end call '%v' exit function", f.desc)
	}
}

// WaitGroupAdd 将全局WaitGroup计数器加n
func WaitGroupAdd(n int) {
	globalWaitGroup.Add(n)
}

// WaitGroupDone 将全局WaitGroup计数器递减1
func WaitGroupDone() {
	globalWaitGroup.Done()
}

// RegisterExceptionHandleFunc 注册异常处理函数
// level:可根据此参数进行fc调用顺序控制
func RegisterExceptionHandleFunc(f func(), desc string, level int) {
	mutex.Lock()
	defer mutex.Unlock()
	exceptionFuncs = append(exceptionFuncs, exceptionFunc{f, desc, level})
}
